package cn.tianyigps.obd.utils;

import cn.tianyigps.obd.beans.BaseMessage;
import cn.tianyigps.obd.beans.LoadMessage;
import cn.tianyigps.obd.constant.AlarmConstant;
import cn.tianyigps.obd.constant.Constant;
import cn.tianyigps.obd.template.JedisTemplate;
import com.google.gson.Gson;
import io.netty.util.internal.StringUtil;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
import redis.clients.jedis.Jedis;

import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * Created with IDEA
 * @description:
 * @author: cui.guibin
 * @create: 2019/3/29 10:52
 **/
public class StringAnalysisUtil {
    private static final char ONE_CHAR = '1';
    /**
     * @Description: 获取Q3无线基站数据(M5部标协议基站数数据也通用)
     * @params: [str]
     * @return: java.lang.String
     * @author: cui.guibin
     * @create: 2019/3/29 10:52
     */
    public static String getBaseStation(String str) {
        Integer index = str.toUpperCase().lastIndexOf("002400A9");


        if (index != -1) {
            //按照指定长度截取出所有基站数据，并获取信号最强的那个基站信息
            Integer stationNumberStart = index + 14;
            Integer stationNumberEnd = index + 16;
            String stationResult = null;
            //获取基站个数
            Integer stationNumber = Integer.parseInt(str.substring(stationNumberStart, stationNumberEnd));
            //遍历出所有的基站数据
            for (int i = 0; i < stationNumber; i++) {
                String stationStr = str.substring(stationNumberEnd + i * 10, stationNumberEnd + (i + 1) * 10);
                Boolean illegal = Integer.parseInt(stationStr.substring(0, 4), 16) == 0 || Integer.parseInt(stationStr.substring(4, 8), 16) == 0;
                //当基站的lac或者cellid有任何一个为0时舍弃
                if (illegal) {
                    continue;
                }
                //当返回数据为空时，将第一个数据存储进去
                if (StringUtils.isEmpty(stationResult)) {
                    stationResult = stationStr;
                }
                //当存储的数据不为空时，判断存储数据的基站的信号强度和现在数据的信号强度，存储信号强度最高的那个
                else {
                    //第八位到第十位为信号强度的数值
                    if (Integer.parseInt(stationStr.substring(8, 10), 16) > Integer.parseInt(stationResult.substring(8, 10), 16)) {
                        stationResult = stationStr;
                    }
                }
            }
            return stationResult;
        } else {
            return null;
        }
    }
    /**
     * @Description: ocar-3设备拥有此字段，获取部标扩展协议的其他数据（非法拆除数据，车架号）
     * @params: [str]
     * @return: java.lang.String
     * @author: cui.guibin
     * @create: 2019/3/29 10:52
     */
    public static String getOther(String str, String equipmentId, Double speed) {
        Map<String, String> map = new HashMap<>();
        Integer illegalDemolitionIndex = str.lastIndexOf("00060089");
        Integer vinIndex = str.lastIndexOf("001300D2");
        Integer iccidIndex = str.lastIndexOf("000C00B2");
        //获取非法拆除
        if (illegalDemolitionIndex != -1) {
            Integer start = illegalDemolitionIndex + 8;
            Integer end = illegalDemolitionIndex + 8 + 8;
            if (str.length() > end) {
                String illegalDemolition = getBinaryString(str.substring(start, end));
                if (Constant.STRING_ZERO.equals(String.valueOf(illegalDemolition.charAt(19)))) {
                    map.put("illegalDemolition", "1");
                }
            }
        }
        //获取车架号
        if (vinIndex != -1) {
            Integer start = vinIndex + 8;
            Integer end = vinIndex + 8 + 34;
            String vin = str.substring(start, end);
            if (!StringUtil.isNullOrEmpty(vin)) {
                map.put("vin", convertHexToStringByGB2312(vin));
            }
        }
        //获取iccid
        if (iccidIndex != -1) {
            Integer start = iccidIndex + 8;
            Integer end = iccidIndex + 28;
            String iccid = str.substring(start, end);
            if (!StringUtil.isNullOrEmpty(iccid)) {
                map.put("iccid", iccid);
            }
        }
        //Map<String, String> loadMsg = getLoadMsg(str, equipmentId);
        Map<String, String> loadMsg = getNewLoadMsg(str, equipmentId, speed);
        map.putAll(loadMsg);
        return map.size() > 0 ? new Gson().toJson(map) : null;
    }

    public static Date getReportTime(String str, int start,int end) {
        String timeStr = getSubString(str, start, end);
        Date date = null;
        try {
            date = new SimpleDateFormat("yyMMddHHmmss").parse(timeStr);
        } catch (Exception e) {
//            log.error("上报数据时间转换错误！", e);
//            log.info(str);
        }
        return date;
    }
    /**
     * @Description: 十六进字符串转GB2312字符串
     * @params: [hex]
     * @return: java.lang.String
     * @author: cui.guibin
     * @create: 2019/3/29 10:52
     */
    public static String convertHexToStringByGB2312(String hex) {
        hex = hex.toUpperCase();
        byte[] bytes = new byte[hex.length() / Constant.INTEGER_TWO];
        int length = hex.length() / Constant.INTEGER_TWO;
        char[] hexChars = hex.toCharArray();
        for (int i = Constant.INTEGER_ZERO; i < length; i++) {
            int pos = i * Constant.INTEGER_TWO;
            bytes[i] = (byte) (charToByte(hexChars[pos]) << Constant.INTEGER_FOUR | charToByte(hexChars[pos + Constant.INTEGER_ONE]));
        }
        String str = null;
        try {
            str = new String(bytes, "GB2312");
        } catch (UnsupportedEncodingException e) {
//            log.error("byte数组通过GB2312解码出错", e.getMessage());
        }
        return str;
    }
    /**
     * @Description: 将字节转换为byte
     * @params: [c]
     * @return: byte
     * @author: cui.guibin
     * @create: 2019/3/29 10:52
     */
    public static byte charToByte(char c) {
        return (byte) "0123456789ABCDEF".indexOf(c);
    }
    /**
     * @Description: 获取航向
     * @params: [str, arr]
     * @return: java.lang.Double
     * @author: cui.guibin
     * @create: 2019/3/29 10:52
     */
    public static String getHeading(String str, int start,int end) {
        String string = getSubString(str, start,end);
        return getDecimalString(string);
    }
    /**
     * @Description: 设备速度
     * @params: [str]
     * @return: java.lang.String
     * @author: cui.guibin
     * @create: 2019/3/29 10:52
     */
    public static Double getSpeed(String str, int start,int end) {
        String string = getSubString(str, start,end);
        String doubleStr = getDecimalString(string);
        return Math.ceil(Double.parseDouble(doubleStr) / 10);
    }
    /**
     * @Description: 获取设备电压包的电压
     * @params: [str]
     * @return: java.lang.String
     * @author: cui.guibin
     * @create: 2019/3/29 10:52
     */
    public static String getVoltage(String str) {
        Integer index = str.lastIndexOf("0004002D");
        String voltage = null;
        if (index != -1) {
            Integer start = index + 8;
            Integer end = index + 12;
            Long num = Long.parseLong(str.substring(start, end), Constant.INTEGER_SIXTEEN);
            voltage = String.valueOf(Double.parseDouble(String.valueOf(num)) / 1000);
        }
        return StringUtil.isNullOrEmpty(voltage) ? null : voltage + "V";
    }
    /**
     * @Description: 获取经纬度
     * @params: [str, arr]
     * @return: java.lang.Double
     * @auther: yangxl
     * @date: 2018/5/25 13:18
     */
    public static Double getLonLat(String str, int start ,int end) {
        String string = getSubString(str, start, end);
        String doubleStr = getDecimalString(string);
        return Double.parseDouble(doubleStr) / (1000000);
    }
    /**
     * @Description: 获取部标协议报警数据
     * @params: [str, arr]
     * @return: java.util.Map
     * @author: cui.guibin
     * @create: 2019/3/29 10:52
     */
    public static Map<String, String> getLocationStatus(String str, int start,int end) {
        String subStr = getSubString(str, start,end);
        //转换后得到的二进制字符串
        String string = getBinaryString(subStr);
        string = new StringBuffer(string).reverse().toString();
        Map<String, String> map = new HashMap();
        map.put("accStatus", "0" + String.valueOf(string.charAt(0)));
        map.put("gpsLocation", String.valueOf(string.charAt(1)));
        //区分南纬北纬（0：北纬）
        map.put("latitude", String.valueOf(string.charAt(2)));
        //区分东经西经（0：东经）
        map.put("longitude", String.valueOf(string.charAt(3)));
        map.put("oilStatus", String.valueOf(string.charAt(10)));
        map.put("ElectricityStatus", String.valueOf(string.charAt(11)));
        return map;
    }
    /**
     * @Description: 获取部标协议报警数据
     * @params: [str, arr]
     * @return: java.lang.String
     * @author: cui.guibin
     * @create: 2019/3/29 10:52
     */
    public static String getAlarm(String str, int start,int end) {
        String alarmStr = null;
        String subStr = getSubString(str, start,end);
        //转换后得到的二进制字符串
        String string = getBinaryString(subStr);
        string = new StringBuffer(string).reverse().toString();
        //超速报警
        if (ONE_CHAR == string.charAt(1)) {
            alarmStr = AlarmConstant.OVERSPEED_ALARM;
        }
        //断电报警
        char c = string.charAt(8);
        if (ONE_CHAR == c) {
            alarmStr = AlarmConstant.POWEROUTAGE_ALARM;
        }
        return alarmStr;
    }

    /**
     * @Description: 此数据为不需要解析的数据，直接截取后返回字符串
     * @params: [str, arr]
     * @return: java.lang.String
     * @author: cui.guibin
     * @create: 2019/3/29 10:52
     */
    public static String getSubString(String str, String[] arr) {
        return str.substring(Integer.parseInt(arr[Constant.INTEGER_ZERO]), Integer.parseInt(arr[Constant.INTEGER_ONE]));
    }
    /**
     * @Description: 此数据为不需要解析的数据，直接截取后返回字符串
     * @params: [str, arr]
     * @return: java.lang.String
     * @author: cui.guibin
     * @create: 2019/3/29 10:52
     */
    public static String getSubString(String str, int start,int end) {
        return str.substring(start, end);
    }

    /**
     * @Description: 将十六进制字符串转换为二进制字符串
     * @params: [buffer, str]
     * @return: java.lang.String
     * @author: cui.guibin
     * @create: 2019/3/29 10:52
     */
    public static String getBinaryString(String str) {
        StringBuilder buffer = new StringBuilder();
        char[] chars = str.toCharArray();
        for (char c : chars) {
            String binStr = Integer.toBinaryString(Integer.parseInt(String.valueOf(c), Constant.INTEGER_SIXTEEN));
            //长度小于4时对数据进行补0
            if (binStr.length() < Constant.INTEGER_FOUR) {
                while (binStr.length() < Constant.INTEGER_FOUR) {
                    // sb.append(str).append(Constant.STRING_ZERO);//右补0
                    binStr = Constant.STRING_ZERO + binStr;// 左补0
                }
            }
            buffer.append(binStr);
        }
        return buffer.toString();
    }

    /**
     * @Description: 获取十进制字符串
     * @params: [str]
     * @return: java.lang.String
     * @author: cui.guibin
     * @create: 2019/3/29 10:52
     */
    public static String getDecimalString(String str) {
        Long num = Long.parseLong(str, Constant.INTEGER_SIXTEEN);
        return num.toString();
    }
    /**
     * @Description: 获取十进制字符串
     * @params: [str]
     * @return: java.lang.String
     * @author: cui.guibin
     * @create: 2019/3/29 10:52
     */
    public static Long getDecimalLong(String str) {
        return Long.parseLong(str, Constant.INTEGER_SIXTEEN);
    }

    /**
     * @Description: 新步长算法计算(陈总方案)
     * @params: [str, equipmentId]
     * @return: java.util.Map
     * @auther: yangxl
     * @date: 2018/12/19 11:34
     */
    private static Map<String, String> getNewLoadMsg(String str, String equipmentId, Double speed) {
        Map<String, String> map = new HashMap<>();
        //扩展数据的下标
        Integer extendIndex = str.toUpperCase().indexOf("EB");
        if (extendIndex != -1) {
            String extendStr = str.substring(extendIndex);
            //获取载重数据的下标
            Integer loadIndex = extendStr.indexOf("00DF");
            if (loadIndex != -1) {
                //myRedisTemplate.delete(equipmentId + "loadMsg");
                //从redis缓存中获取载重数据的参数
                Jedis jedis = JedisTemplate.getJedis();
                String loadMessage = jedis.get(equipmentId + "loadMsg");
                Gson gson = new Gson();
                LoadMessage loadMsg = gson.fromJson(loadMessage, LoadMessage.class);

                //当缓存的数据不存在时从php接口获取
                if (!Optional.ofNullable(loadMsg).isPresent()) {
                    //查询参数
                    Map<String, String> params = new HashMap(1);
                    params.put("equipmentId", equipmentId);

                    RestTemplate restTemplate = new RestTemplate();
                    String url = PropertiesUtil.getStr("load.message.url", "constant.properties");
                    if (StringUtil.isNullOrEmpty(url)){
                        // TODO 错误日志
                        return null;
                    }
                    BaseMessage message = restTemplate.getForObject(url, BaseMessage.class, params);
                    if (null != message && null != message.getData()) {
                        loadMsg = message.getData();
                        String loadStr = gson.toJson(loadMsg);
                        jedis.set(equipmentId + "loadMsg", loadStr,"NX","EX", 1800);
                    }
                }

                String loadStr = extendStr.substring(loadIndex);
                //获取载重外设厂家标识，两个byte的ascii的英文首字母
                Integer merchantsIndex = loadStr.indexOf("00F1");
                String merchants = convertHexToStringByGB2312(str.substring(merchantsIndex + 6, merchantsIndex + 10));
                map.put("merchants", merchants);
                //获取流水号
                Integer f2Index = merchantsIndex + 10;
                String serialNumber = loadStr.substring(f2Index + 6, f2Index + 14);
                map.put("serialNumber", serialNumber);
                //获取软件版本号
                Integer f3Index = f2Index + 14;
                String softWareV = loadStr.substring(f3Index + 6, f3Index + 14);
                StringBuilder softWareVersion = new StringBuilder("v");
                for (int i = 0; i < 4; i++) {
                    String s = getDecimalString(softWareV.substring(i * 2, (i + 1) * 2));
                    softWareVersion.append(s).append(".");
                }
                map.put("softWareVersion", softWareVersion.substring(0, softWareVersion.length() - 1));
                //获取硬件版本
                Integer f4Index = f3Index + 14;
                String hardWareVersion = loadStr.substring(f4Index + 6, f4Index + 8).charAt(0) + ".0";
                map.put("hardWareVersion", hardWareVersion);
                //获取硬件状态
                Integer f5Index = f4Index + 8;
                String hardWareStatus = loadStr.substring(f5Index + 6, f5Index + 12);
                map.put("hardWareStatus", hardWareStatus);
                String binaryString = getBinaryString(hardWareStatus.substring(0, 4));
                //判断载重设备的数量
                int count = 0;
                int index = 0;
                while ((index = binaryString.indexOf("00", index)) != -1) {
                    index = index + "00".length();
                    count++;
                }
                map.put("loadEquipNum", String.valueOf(count));
                //获取软件状态
                Integer f6Index = f5Index + 12;
                String softWareStatus = loadStr.substring(f6Index + 6, f6Index + 10);
                map.put("softWareStatus", softWareStatus);
                //获取载重量输出值
                Integer f7Index = f6Index + 10;
                Integer loadOutput = Integer.parseInt(loadStr.substring(f7Index + 6, f7Index + 14), Constant.INTEGER_SIXTEEN);
                map.put("loadOutput", String.valueOf(loadOutput));
                //获取原始AD值
                Integer f8Index = f7Index + 14;
                Integer f8Length = Integer.parseInt(loadStr.substring(f8Index + 4, f8Index + 6), 16);
                String originalAD = getDecimalString(loadStr.substring(f8Index + 6, f8Index + 6 + f8Length * 2)).substring(0, 5);
                //获取毛重
                if (null != loadMsg) {
                    if (Optional.ofNullable(loadMsg.getNoLoadAD()).isPresent() && Optional.ofNullable(loadMsg.getFullLoadAD()).isPresent() && Optional.ofNullable(loadMsg.getDeactivationConstant()).isPresent()
                            && Optional.ofNullable(loadMsg.getFullLoadWeight()).isPresent() && Optional.ofNullable(loadMsg.getWeightOfCar()).isPresent()) {

                        Double f8Weight = loadMsg.getFullLoadWeight() * ((Integer.parseInt(originalAD, 16) - loadMsg.getF8NoLoadAD()) / (loadMsg.getF8FullLoadAD() - loadMsg.getF8NoLoadAD())) + loadMsg.getWeightOfCar();
                        map.put("f8", f8Weight < 0 ? "0" : f8Weight.toString());
                    }
                }
                map.put("originalAD", originalAD);
                //获取处理AD值
                Integer f9Index = f8Index + 6 + f8Length * 2;
                Integer f9Length = Integer.parseInt(loadStr.substring(f9Index + 4, f9Index + 6), 16);
                String handleAD = getDecimalString(loadStr.substring(f9Index + 6, f9Index + 6 + f9Length * 2)).substring(0, 5);
                map.put("handleAD", handleAD);
                if (null != loadMsg) {
                    //从redis缓存中F11的数据
                    Integer f11 = Integer.valueOf( jedis.get(equipmentId + "newF11"));
                    Long time = jedis.ttl(equipmentId + "newF11");
                    //存储三个点，用来判断削峰数据
                    Map<String, Double> points = new HashMap<>();

                    //单精度设备
                    if (1 == loadMsg.getEquipCount() && Optional.ofNullable(loadMsg.getDeactivationConstant()).isPresent()) {
                        getDragOne(equipmentId, map, loadMsg, handleAD, f11, time, points);
                    }
                    //单精度设备
                    if (2 == loadMsg.getEquipCount() && Optional.ofNullable(loadMsg.getDeactivationConstant()).isPresent()) {
                        getDragOne(equipmentId, map, loadMsg, handleAD, f11, time, points);
                    }
                    //高精度设备
                    if (4 == loadMsg.getEquipCount()) {
                        getDragFour(equipmentId, speed, map, loadMsg, loadOutput, f11, points);
                    }
                    //高精度设备
                    if (6 == loadMsg.getEquipCount()) {
                        getDragFour(equipmentId, speed, map, loadMsg, loadOutput, f11, points);
                    }

                    LoadCutPeakUtil.cutPeakWeightByThreePoint(points, loadMsg, speed, map);

                }
                //获取车牌号
                Integer faIndex = f9Index + 6 + f9Length * 2;
                Integer faLength = Integer.parseInt(loadStr.substring(faIndex + 4, faIndex + 6), 16);
                String plateNumbers = convertHexToStringByGB2312(loadStr.substring(faIndex + 6, faIndex + 6 + faLength * 2));
                map.put("plateNumbers", plateNumbers);
                //获取设备号
                Integer fbIndex = faIndex + 6 + faLength * 2;
                String id = loadStr.substring(fbIndex + 6, fbIndex + 14);
                map.put("equipmentId", id);
                //获取形变参数
                Integer fcIndex = fbIndex + 14;
                String shapeChange = loadStr.substring(fcIndex + 6, fcIndex + 14);
                map.put("shapeChange", shapeChange);
            }
        }
        return map;
    }


    /**
     * @Description: 一拖一获取毛重的解决方案
     * @params: [equipmentId, map, loadMsg, handleAD, f11, time]
     * @return: java.util.Map
     * @auther: yangxl
     * @date: 2019/2/23 10:01
     */
    private static void getDragOne(String equipmentId, Map<String, String> map,
                            LoadMessage loadMsg, String handleAD, Integer f11, Long time, Map<String, Double> points) {

        Jedis jedis = JedisTemplate.getJedis();
        Double one = Double.valueOf(jedis.get(equipmentId + "one"));
        Double two = Double.valueOf(jedis.get(equipmentId + "two"));

        Integer f10 = Integer.valueOf(handleAD) / loadMsg.getDeactivationConstant();
        if (!Optional.ofNullable(f11).isPresent() || time <= 0) {
            f11 = f10;
            jedis.set(equipmentId + "newF11", f11.toString(),"NX","EX", loadMsg.getResetTime() * 60);
            time = jedis.ttl(equipmentId + "newF11");
        }
        //获取载重协议计算公式数据t参数
//        if (f11 > f10) {
//            f11 -= loadMsg.getStepSize();
//        }
//        if (f11 < f10) {
//            f11 += loadMsg.getStepSize();
//        }
        //获取毛重
        if (Optional.ofNullable(loadMsg.getNoLoadAD()).isPresent() && Optional.ofNullable(loadMsg.getFullLoadAD()).isPresent() && Optional.ofNullable(loadMsg.getDeactivationConstant()).isPresent()
                && Optional.ofNullable(loadMsg.getFullLoadWeight()).isPresent() && Optional.ofNullable(loadMsg.getWeightOfCar()).isPresent()) {
            Double netWeight = loadMsg.getFullLoadWeight() * ((f11 - (loadMsg.getNoLoadAD() / loadMsg.getDeactivationConstant())) / ((loadMsg.getFullLoadAD() / loadMsg.getDeactivationConstant()) - (loadMsg.getNoLoadAD() / loadMsg.getDeactivationConstant()))) /*+ loadMsg.getWeightOfCar()*/;
            //毛重数据
            Double weight = Double.parseDouble(String.valueOf(new Formatter().format("%.2f", netWeight)));
            if (weight < 0 || weight < loadMsg.getFullLoadWeight() * (loadMsg.getZeroTrack() / 100.0)) {
                weight = 0.0;
            }
            map.put("netWeight", weight.toString());
            jedis.set(equipmentId + "one", two.toString());
            jedis.set(equipmentId + "two", weight.toString());
            points.put("one", one);
            points.put("two", two);
            points.put("three", weight);
        }
        jedis.set(equipmentId + "newF11","NX","EX", f11.toString(), time);
    }


    /**
     * @Description: 一拖四获取毛重值解决方案
     * @params: [equipmentId, speed, map, loadMsg, loadOutput, f11]
     * @return: java.util.Map
     * @auther: yangxl
     * @date: 2019/2/23 10:12
     */
    private static void getDragFour(String equipmentId, Double speed,
                             Map<String, String> map, LoadMessage loadMsg, Integer loadOutput, Integer f11, Map<String, Double> points) {

        Jedis jedis = JedisTemplate.getJedis();
        Double one = Double.valueOf(jedis.get(equipmentId + "one"));
        Double two = Double.valueOf(jedis.get(equipmentId + "two"));

        if (!Optional.ofNullable(f11).isPresent()) {
            f11 = 0;
            jedis.set(equipmentId + "newF11", f11.toString());
        }
        //判断车辆类型是环卫车还是物流车
            /*if (("logistic").equals(loadMsg.getCarType())) {
                f11 = (f11 + loadOutput) >> 1;
            } else if (("clean").equals(loadMsg.getCarType())) {
                //环卫车出现重量下降时不做数据平滑处理
                if (f11 > loadOutput) {
                    f11 = loadOutput;
                } else {
                    f11 = (f11 + loadOutput) >> 1;
                }
            }*/
        //颠簸重量数据，只有速度大于10时有用

        f11 = loadOutput;
        jedis.set(equipmentId + "newF11", f11.toString());
        //判断是运动状态还是静止状态
        if (null != speed && speed > 0) {
            f11 = ((int) Math.floor(f11 / loadMsg.getRunStepSize())) * loadMsg.getRunStepSize();
        } else {
            f11 = ((int) Math.floor(f11 / loadMsg.getStepSize())) * loadMsg.getStepSize();
        }

        jedis.set(equipmentId + "one", two.toString());
        jedis.set(equipmentId + "two", f11.toString());
        points.put("one", one);
        points.put("two", two);
        points.put("three", Double.parseDouble(String.valueOf(f11)));
        map.put("netWeight", String.valueOf(f11));

    }
}
